package com.fenyin.samples.study.jdk.nio.channels.file;

import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.channels.Channels;
import java.nio.channels.ReadableByteChannel;
import java.nio.channels.WritableByteChannel;

import com.fenyin.samples.study.jdk.nio.NIOLogUtil;

public class ChannelCopy {
	/**
	 * This code copies data from stdin to stdout. Like the 'cat' command, but
	 * without any useful options.
	 */
	public static void main(String[] argv) throws IOException {
		System.out.println("channel-copy-step1");
		ReadableByteChannel source = Channels.newChannel(System.in);
		System.out.println("channel-copy-step2");
		WritableByteChannel dest = Channels.newChannel(System.out);
		System.out.println("channel-copy-step3");
		channelCopy1(source, dest);
		System.out.println("channel-copy-step4");
		// alternatively, call channelCopy2 (source, dest);
		source.close();
		dest.close();
	}

	/**
	 * Channel copy method 1. This method copies data from the src channel and
	 * writes it to the dest channel until EOF on src. This implementation makes
	 * use of compact( ) on the temp buffer to pack down the data if the buffer
	 * wasn't fully drained. This may result in data copying, but minimizes
	 * system calls. It also requires a cleanup loop to make sure all the data
	 * gets sent.
	 */
	private static void channelCopy1(ReadableByteChannel src,WritableByteChannel dest) throws IOException {
//		ByteBuffer buffer = ByteBuffer.allocateDirect(16 * 1024);
		ByteBuffer buffer = ByteBuffer.allocateDirect(10);
		int count = 1;
		int num   = 1;
		NIOLogUtil.print("channelCopy1-step1:begin",buffer);
		while (src.read(buffer) != -1) {
			// Prepare the buffer to be drained
			NIOLogUtil.print("channelCopy1-step1-read-"+count+":end", buffer);
			buffer.flip();
			NIOLogUtil.print("channelCopy1-step1-flip-"+count+":end", buffer);
			// Write to the channel; may block
			dest.write(buffer);
			NIOLogUtil.print("channelCopy1-step1-write-"+count+":end", buffer);
			// If partial transfer, shift remainder down
			// If buffer is empty, same as doing clear( ) 60
			buffer.compact();
			NIOLogUtil.print("channelCopy1-step1-compact-"+count+":end", buffer);
			count++;
		}
		// EOF will leave buffer in fill state
		NIOLogUtil.print("channelCopy1-step1:end",buffer);
		buffer.flip();
		NIOLogUtil.print("channelCopy1-step2:end",buffer);
		// Make sure that the buffer is fully drained
		while (buffer.hasRemaining()) {
			dest.write(buffer);
			NIOLogUtil.print("channelCopy1-step3-write-"+num+":end", buffer);
			num++;
		}
		
		NIOLogUtil.print("channelCopy1-step3:end",buffer);
	}

	/**
	 * Channel copy method 2. This method performs the same copy, but assures
	 * the temp buffer is empty before reading more data. This never requires
	 * data copying but may result in more systems calls. No post-loop cleanup
	 * is needed because the buffer will be empty when the loop is exited.
	 */
	@SuppressWarnings("unused")
	private static void channelCopy2(ReadableByteChannel src,WritableByteChannel dest) throws IOException {
//		ByteBuffer buffer = ByteBuffer.allocateDirect(16 * 1024);
		ByteBuffer buffer = ByteBuffer.allocateDirect(10);
		int count = 1;
		NIOLogUtil.print("channelCopy2-step1:begin",buffer);
		while (src.read(buffer) != -1) {
			NIOLogUtil.print("channelCopy2-step1-read-"+count+":end", buffer);
			// Prepare the buffer to be drained
			buffer.flip();
			NIOLogUtil.print("channelCopy2-step1-flip-"+count+":end", buffer);
			// Make sure that the buffer was fully drained
			int num =1;
			while (buffer.hasRemaining()) {
				dest.write(buffer);
				NIOLogUtil.print("channelCopy2-step2-write-"+count+"-"+num+":end", buffer);
				num++;
			}
			NIOLogUtil.print("channelCopy2-step1-write-"+count+":end", buffer);
			// Make the buffer empty, ready for filling
			buffer.clear();
			NIOLogUtil.print("channelCopy2-step1-clear-"+count+":end", buffer);
		}
	}
}